package com.zhaowenhao.hello.spring.framework.webmvc;

import com.zhaowenhao.hello.spring.framework.annotation.WHAutowired;
import com.zhaowenhao.hello.spring.framework.annotation.WHController;
import com.zhaowenhao.hello.spring.framework.annotation.WHRequestMapping;
import com.zhaowenhao.hello.spring.framework.annotation.WHService;
import lombok.SneakyThrows;
import lombok.val;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.swing.text.StyledEditorKit;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @Description:
 * @Author: zwh
 * @Date: 2020-07-24
 * @Time: 16:43
 */
public class WHDispatcherServlet extends HttpServlet {

    //ioc容器
    private HashMap<String, Object> ioc = new HashMap<String, Object>();

    //handlerMapping
    private HashMap<String, Object> mapping = new HashMap<String, Object>();


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("hello-------------------------------------------------");
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req, resp);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        uri = uri.replace(contextPath, "").replaceAll("/+", "/");
        if (!mapping.containsKey(uri)) {
            resp.getWriter().print("404!");
            return;
        }

        Map<String, String[]> parameterMap = req.getParameterMap();
        Method method = (Method) mapping.get(uri);
        method.invoke(ioc.get(method.getDeclaringClass().getName()), req, resp, parameterMap.get("name")[0],
                parameterMap.get("id")[0]);
    }


    @SneakyThrows
    @Override
    public void init(ServletConfig config) throws ServletException {
        //加载配置文件
        InputStream is = null;
        Properties configContext = new Properties();
        String configLocation = config.getInitParameter("contextConfigLocation");
        configLocation = configLocation.replace("classpath:", "/");

        is = this.getClass().getResourceAsStream(configLocation);
        try {
            configContext.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String scanPackage = configContext.getProperty("scanPackage");

        //扫描配置文件中的路径下所有的className
        doScanPackage(scanPackage);

        //装载HandlerMapping
        for (String className : ioc.keySet()) {
            if (!className.contains(".")) {
                continue;
            }
            Class<?> clazz = Class.forName(className);
            if (clazz.isAnnotationPresent(WHController.class)) {
                //装载到ioc
                ioc.put(className, clazz.newInstance());

                String baseUrl = "";

                if (clazz.isAnnotationPresent(WHRequestMapping.class)) {
                    WHRequestMapping annotation = clazz.getAnnotation(WHRequestMapping.class);
                    baseUrl = annotation.value();
                }
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(WHRequestMapping.class)) {
                        continue;
                    }
                    WHRequestMapping whRequestMapping = method.getAnnotation(WHRequestMapping.class);
                    String url = whRequestMapping.value();
                    baseUrl = (baseUrl + "/" + url).replaceAll("/+", "/");
                    mapping.put(baseUrl, method);
                    System.out.println("Mapped" + baseUrl + "," + method);
                }

            } else if (clazz.isAnnotationPresent(WHService.class)) {
                //遍历所有加了WHService注解的类，加入IOC中
                WHService service = clazz.getAnnotation(WHService.class);
                String beanName = service.value();
                if ("".equals(beanName)) {
                    beanName = clazz.getName();
                }
                Object newInstance = clazz.newInstance();
                ioc.put(beanName, newInstance);

                //同时为所有接口赋予相应的实例对象
                Class<?>[] interfaces = clazz.getInterfaces();
                for (Class<?> inter : interfaces) {
                    ioc.put(inter.getName(), newInstance);
                }

            } else {
                continue;
            }
        }
        Collection<Object> values = ioc.values();
        for (Object object : values) {
            if (object == null) {
                continue;
            }
            Class<?> clazz = object.getClass();
            if (clazz.getAnnotation(WHController.class) == null) {
                continue;
            }
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (!field.isAnnotationPresent(WHAutowired.class)) {
                    continue;
                }
                WHAutowired autowired = field.getAnnotation(WHAutowired.class);
                String beanName = autowired.value();
                if ("".equals(beanName)) {
                    beanName = field.getType().getName();
                }
                field.setAccessible(true);
                field.set(ioc.get(clazz.getName()), ioc.get(beanName));
            }
        }
    }


    private void doScanPackage(String scanPackage) {
        URL url = this.getClass().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        File file = new File(url.getFile());
        for (File f : file.listFiles()) {
            if (f.isDirectory()) {
                doScanPackage(scanPackage + "." + f.getName());
            } else {
                if (!f.getName().endsWith(".class")) {
                    continue;
                }
                String clazzName = scanPackage + "." + f.getName().replace(".class", "");
                ioc.put(clazzName, null);
            }
        }
    }
}
